using System;
using System.Xml;
using BLGenerator.BusinessLogic;

namespace BLGenerator.DataLayer
{
    public class XmlIOModule : IOModule
    {
        private readonly XmlReader _xmlReader;
        private readonly XmlWriter _xmlWriter;

        public XmlIOModule(string filename, bool forReading)
        {
            if (forReading)
            {
                XmlReaderSettings settings = new XmlReaderSettings();
                _xmlReader = XmlReader.Create(filename, settings);
            }
            else
            {
                XmlWriterSettings settings = new XmlWriterSettings();
                settings.Indent = true;
                _xmlWriter = XmlWriter.Create(filename, settings);
            }
        }

        #region Open methods

        public override void OpenProject(Project project)
        {
            _xmlReader.Read();
            _xmlReader.Read();
            while (_xmlReader.Read())
            {
                switch (_xmlReader.NodeType)
                {
                    case XmlNodeType.Element:
                        switch (_xmlReader.Name)
                        {
                            case "Settings":
                                ReadSettingsSection(project);
                                break;
                            case "Entities":
                                ReadEntitiesSection(project);
                                break;
                        }
                        break;
                }
            }

            _xmlReader.Close();
        }

        protected void ReadSettingsSection(Project project)
        {
            while (_xmlReader.Read())
            {
                if (_xmlReader.NodeType == XmlNodeType.EndElement) return;
                if (_xmlReader.NodeType != XmlNodeType.Element) continue;
                string settingName = _xmlReader["Name"];
                string typeName = _xmlReader["Type"];
                string valueString = _xmlReader["Value"];
                Type type = Type.GetType(typeName);
                System.Reflection.MethodBase method = type.GetMethod("GetValueFromString", new Type[] { typeof(string) });
                object value = method.Invoke(null, new object[] { valueString });
                project.SettingValues[settingName] = value;
            }
        }

        protected void ReadEntitiesSection(Project project)
        {
            Entity entity = null;
            while (_xmlReader.Read())
            {
                if (_xmlReader.NodeType == XmlNodeType.EndElement)
                {
                    if (entity == null) return;
                    else entity = null;
                }
                if (_xmlReader.NodeType != XmlNodeType.Element) continue;
                if (_xmlReader.Name == "Entity")
                {
                    string entityName = _xmlReader["Name"];
                    string tableName = _xmlReader["TableName"];
                    bool isGenerated = bool.Parse(_xmlReader["IsGenerated"]);
                    entity = new Entity(entityName, tableName, isGenerated, project);
                    project.EntityCollection.Add(entity);
                }
                else if (_xmlReader.Name == "Fields")
                    ReadFieldsSection(entity);
                else if (_xmlReader.Name == "PrimaryFields")
                    ReadPrimaryFieldsSection(entity);
            }
        }

        protected void ReadFieldsSection(Entity entity)
        {
            while (_xmlReader.Read())
            {
                if (_xmlReader.NodeType == XmlNodeType.EndElement) return;
                if (_xmlReader.NodeType != XmlNodeType.Element) continue;
                if (_xmlReader.Name == "Field")
                {
                    string fieldName = _xmlReader["FieldName"];
                    string propertyName = _xmlReader["PropertyName"];
                    string columnName = _xmlReader["ColumnName"];
                    bool isNullable = bool.Parse(_xmlReader["IsNullable"]);
                    Type type = Type.GetType(_xmlReader["TypeName"]);
                    bool isAutoGenerated = bool.Parse(_xmlReader["IsAutoGenerated"]);
                    entity.FieldCollection.Add(new Field(fieldName, propertyName, columnName, type, isAutoGenerated, isNullable));
                }
            }
        }

        protected void ReadPrimaryFieldsSection(Entity entity)
        {
            while (_xmlReader.Read())
            {
                if (_xmlReader.NodeType == XmlNodeType.EndElement) return;
                if (_xmlReader.NodeType != XmlNodeType.Element) continue;
                if (_xmlReader.Name == "PrimaryField")
                {
                    string columnName = _xmlReader["ColumnName"];
                    Field field = entity.FieldCollection.FindByColumnName(columnName);
                    entity.PrimaryFields.Add(field);
                }
            }
        }

        #endregion

        #region Save methods

        public override void SaveProject(Project project)
        {
            _xmlWriter.WriteStartElement("Project");

            WriteSettingsSection(project);
            WriteEntitiesSection(project);

            _xmlWriter.WriteFullEndElement();

            _xmlWriter.Close();
        }

        protected void WriteSettingsSection(Project project)
        {
            _xmlWriter.WriteStartElement("Settings");
            foreach (ProjectSetting setting in project.Settings)
            {
                _xmlWriter.WriteStartElement("Setting");
                _xmlWriter.WriteAttributeString("Name", setting.Name);
                _xmlWriter.WriteAttributeString("Type", setting.GetType().ToString());
                _xmlWriter.WriteAttributeString("Value", setting.GetStringFromValue(project.SettingValues[setting.Name]));
                _xmlWriter.WriteEndElement();
            }
            _xmlWriter.WriteFullEndElement();
           
        }

        protected void WriteEntitiesSection(Project project)
        {
            _xmlWriter.WriteStartElement("Entities");
            foreach (Entity entity in project.EntityCollection)
            {
                _xmlWriter.WriteStartElement("Entity");
                _xmlWriter.WriteAttributeString("Name", entity.EntityName);
                _xmlWriter.WriteAttributeString("TableName", entity.TableName);
                _xmlWriter.WriteAttributeString("IsGenerated", entity.IsGenerated.ToString());

                if (entity.FieldCollection.Count > 0)
                {
                    _xmlWriter.WriteStartElement("Fields");
                    foreach (Field field in entity.FieldCollection)
                    {
                        _xmlWriter.WriteStartElement("Field");
                        _xmlWriter.WriteAttributeString("FieldName", field.FieldName);
                        _xmlWriter.WriteAttributeString("PropertyName", field.PropertyName);
                        _xmlWriter.WriteAttributeString("ColumnName", field.ColumnName);
                        _xmlWriter.WriteAttributeString("TypeName", field.Type.ToString());
                        _xmlWriter.WriteAttributeString("IsAutoGenerated", field.IsAutoGenerated.ToString());
                        _xmlWriter.WriteAttributeString("IsNullable", field.IsNullable.ToString());
                        _xmlWriter.WriteEndElement();
                    }
                    _xmlWriter.WriteFullEndElement();
                }

                if (entity.PrimaryFields.Count > 0)
                {
                    _xmlWriter.WriteStartElement("PrimaryFields");
                    foreach (Field field in entity.PrimaryFields)
                    {
                        _xmlWriter.WriteStartElement("PrimaryField");
                        _xmlWriter.WriteAttributeString("ColumnName", field.ColumnName);
                        _xmlWriter.WriteEndElement();
                    }
                    _xmlWriter.WriteFullEndElement();
                }

                _xmlWriter.WriteFullEndElement();
            }
            _xmlWriter.WriteFullEndElement();
        }

        #endregion
    }
}